#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>

#include "ncurses.h"

#include "menus.h"
#include "saisie_clavier_ncurses.h"
#include "affichage.h"
#include "parsing.h"

#include "constantes_et_macros.h"




/**
 * \fn static void afficher_titre_menu(struct menu_texte* menu, unsigned int* pos_y)
 * \brief Affiche le titre d'un menu dans la fenetre a laquelle il est associe
 *
 * \param[in,out] menu L'adresse du menu concerne
 * \param[in,out] pos_y L'adresse du numero de ligne auquel le titre sera affiche
 *
 * \warning *pos_y sera incremente, comme pour afficher_dans_boite.
 */
static void afficher_titre_menu(struct menu_texte* menu, unsigned int* pos_y)
{
    unsigned int dim_x;

    dim_x = getmaxx(menu->fenetre);
    wattron(menu->fenetre, A_UNDERLINE | A_BOLD);
    mvwprintw(menu->fenetre, *pos_y, (dim_x - strlen(menu->titre)) / 2, "%s", menu->titre);
    wattroff(menu->fenetre, A_UNDERLINE | A_BOLD);
    *pos_y = *pos_y + 1;
}



/**
 * \fn static void init_menu_texte(struct menu_texte* menu,
                                   const unsigned int taille,
                                   const unsigned int nb_affiches,
                                   const char* titre,
                                   fenetre_texte* fenetre)
 * \brief Initialise un menu en lui donnant une certaine capacite
 *
 * \param[out] menu L'adresse du menu a initialiser
 * \param[in] taille La capacite a donner au menu
 * \param[in] nb_affiches Le nombre de choix qui pourront etre affiches simultanement
 * \param[in] titre Le titre du menu. Il sera affiche
 * \param[in] fenetre L'adresse de la fenetre dans laquelle le menu va etre utilise
 *
 * \warning Si la valeur de nb_affiches est superieure a taille, taille sera
 *          utilisee a la place. En effet, on ne peut pas afficher plus de
 *          choix que ce que contient le menu.
 *
 * Notez que la taille du menu ne pourra pas changer par la suite.\n
 * Si une taille nulle est demandee, le champ tab du menu vaudra NULL.
 */
static void init_menu_texte(struct menu_texte* menu,
                            const unsigned int taille,
                            const unsigned int nb_affiches,
                            const char* titre,
                            fenetre_texte* fenetre)
{
    if(taille > 0)
    {
        if(!(menu->tab = (struct choix*)malloc(taille * sizeof(struct choix))))
            exit(EXIT_MALLOC);
    }
    else
        menu->tab = NULL;
    menu->nb_choix = 0;
    menu->indice_premier_choix = 0;
    menu->position_curseur = 0;
    strcpy(menu->titre, titre);
    menu->fenetre = fenetre;
    /*
     * on fait en sorte qu'il ne soit pas demande
     * d'afficher plus de choix qu'il n'en existe
     */
    if(nb_affiches > taille)
        menu->nb_choix_affiches = taille;
    else
        menu->nb_choix_affiches = nb_affiches;
}



/**
 * \fn static void detruire_menu_texte(const struct menu_texte* menu)
 * \brief Libere la memoire allouee pour un menu_texte
 *
 * \param[in] menu L'adresse du menu a detruire
 */
static void detruire_menu_texte(const struct menu_texte* menu)
{
    if(menu->tab)
        free(menu->tab);
}



/**
 * \fn static void ajouter_choix(struct menu_texte* menu, const char* texte, const int valeur)
 * \brief Ajoute un choix a un menu
 *
 * \param[in,out] menu L'adresse du menu concerne
 * \param[in] texte Le texte a associer au choix
 * \param[in] valeur La valeur a associer au choix
 *
 * \warning Attention a ne pas depasser la capacite du menu !\n
 *          Sachez que les espaces a la fin du texte seront retires.
 */
static void ajouter_choix(struct menu_texte* menu, const char* texte, const int valeur)
{
    char* texte_choix;
    int i;

    texte_choix = menu->tab[menu->nb_choix].texte;
    strcpy(menu->tab[menu->nb_choix].texte, texte);
    menu->tab[menu->nb_choix].valeur = valeur;
    /*
     * il se peut que le texte du choix se termine par des espaces,
     * ce qui n'est pas tres esthetique. On les enleve donc ici
     */
    i = strlen(texte_choix) - 1;
    while(texte_choix[i] == ' ' && i >= 0)
        i--;
    texte_choix[i + 1] = '\0';
    menu->nb_choix++;
}



/**
 * \fn static void afficher_menu_texte(struct menu_texte* menu)
 * \brief Affiche un menu
 *
 * \param[in] menu L'adresse du menu concerne
 *
 * Dans certains cas, cette procedure n'est pas en mesure d'afficher tous les
 * choix du menu. Elle en affiche alors une selection qui depend de la position
 * du curseur (pas celui de ncurses, mais celui qui indique le choix du joueur)
 * et ses mouvements precedents.\n
 * Notez que le titre sera affiche et que le choix correspondant a la position
 * du curseur sera mis en evidence.
 */
static void afficher_menu_texte(struct menu_texte* menu)
{
    unsigned int pos_x, pos_y;
    int indice_choix;
    fenetre_texte* fenetre;

    fenetre = menu->fenetre;
    /* on se procure la position verticale de depart */
    getyx(fenetre, pos_y, pos_x);
    /*
     * indice_choix est la variable permettant de parcourir
     * les choix, et non celle designant le choix du joueur
     */
    indice_choix = menu->indice_premier_choix;
    afficher_titre_menu(menu, &pos_y);
    /* incrementer pos_y revient a sauter une ligne */
    pos_y++;
    /* fleche facultative indiquant la presence d'autres choix plus haut */
    if(menu->indice_premier_choix > 0)
        afficher_dans_boite(fenetre, &pos_y, "       ^       ");
    else
        pos_y++;
    /* affichage des choix */
    for(;  indice_choix < menu->indice_premier_choix + menu->nb_choix_affiches;  indice_choix++)
    {
        if(indice_choix == menu->position_curseur)
        {
            wattron(fenetre, A_REVERSE);
            afficher_dans_boite(fenetre, &pos_y, menu->tab[indice_choix].texte);
            wattroff(fenetre, A_REVERSE);
        }
        else
            afficher_dans_boite(fenetre, &pos_y, menu->tab[indice_choix].texte);
    }
    /* fleche facultative indiquant la presence d'autres choix plus bas */
    if(indice_choix < menu->nb_choix)
        afficher_dans_boite(fenetre, &pos_y, "       v       ");
    wrefresh(fenetre);
}



/**
 * \fn static char saisir_menu_texte(struct menu_texte* menu)
 * \brief Permet a l'utilisation de naviguer dans un menu au moyen des fleches
 *        du haut et du bas, de la touche entree, et de la touche echap
 *
 * \param[in,out] menu L'adresse du menu concerne
 * \return FLECHE_HAUT, FLECHE_DROITE, FLECHE_BAS, FLECHE_GAUCHE,
 *         ENTREE, ou ECHAP, en fonction de la touche sur laquelle
 *         l'utilisateur a appuye
 */
static char saisir_menu_texte(struct menu_texte* menu)
{
    /* a peu pret la meme chose que pour la saisie clavier ncurses */
    do
    {
        switch(wgetch(menu->fenetre))
        {
            case KEY_UP:
                /* deplacement du curseur */
                if(menu->position_curseur == 0)
                    menu->position_curseur = menu->nb_choix - 1;
                else
                    menu->position_curseur--;
                /* deplacement du champ de vision des choix */
                /*
                 * s'il y a plus de 3 choix affiches,
                 * on decale le champ de vision plus tot.
                 * Il faut egalement regarder si on n'est pas
                 * deja tout en haut ou tout en bas du menu
                 */
                if(menu->nb_choix_affiches > 2 &&
                   menu->indice_premier_choix > 0)
                {
                    if(menu->position_curseur < menu->indice_premier_choix + 1)
                        menu->indice_premier_choix--;
                }
                else
                {
                    if(menu->position_curseur < menu->indice_premier_choix)
                        menu->indice_premier_choix--;
                }
                while(menu->position_curseur > menu->indice_premier_choix + menu->nb_choix_affiches - 1)
                    menu->indice_premier_choix++;
                return FLECHE_HAUT;
                break;
            case KEY_DOWN:
                /* deplacement du curseur */
                if(menu->position_curseur == menu->nb_choix - 1)
                    menu->position_curseur = 0;
                else
                    menu->position_curseur++;
                /* deplacement du champ de vision des choix */
                if(menu->nb_choix_affiches > 2 &&
                   menu->indice_premier_choix + menu->nb_choix_affiches < menu->nb_choix)
                {
                    if(menu->position_curseur > menu->indice_premier_choix + menu->nb_choix_affiches - 2)
                    menu->indice_premier_choix++;
                }
                else
                {
                    if(menu->position_curseur > menu->indice_premier_choix + menu->nb_choix_affiches - 1)
                        menu->indice_premier_choix++;
                }
                while(menu->position_curseur < menu->indice_premier_choix)
                    menu->indice_premier_choix--;
                return FLECHE_BAS;
                break;
            case 10:
                return ENTREE;
                break;
            case 27:
                return ECHAP;
                break;
            /*
             * la touche echap semblant bloquer ncurses pendant presque deux
             * secondes, on permet a l'utilisateur d'utiliser
             * backspace a la place
             */
            case KEY_BACKSPACE:
                return ECHAP;
                break;
            default:
                break;
        }
    } while(VRAI);
}



/**
 * \fn static int get_valeur_choix(const struct menu_texte* menu)
 * \brief Permet de connaitre la valeur associee au choix
 *        actuellement associe au curseur dans un menu
 *
 * \param[in] menu Le menu concerne;
 * \return L'entier associe au choix d'indice menu->position_curseur
 */
static int get_valeur_choix(const struct menu_texte* menu)
{
    return menu->tab[menu->position_curseur].valeur;
}



/**
 * \fn static unsigned int get_position_curseur(const struct menu_texte* menu)
 * \brief Obtient l'indice du choix actuellement pointe par le joueur
 *
 * \param[in] menu L'adresse du menu concerne
 * \return L'indice (entier positif ou nul) du choix pointe par le joueur
 */
static unsigned int get_position_curseur(const struct menu_texte* menu)
{
    return menu->position_curseur;
}



/**
 * \fn static void set_position_curseur(struct menu_texte* menu,
                                        unsigned int position)
 * \brief Modifie l'indice du choix pointe par le joueur
 *
 * \param[out] menu L'adresse du menu concerne
 * \param[in] position L'indice du choix que l'on souhaite faire pointer
 */
static void set_position_curseur(struct menu_texte* menu,
                                 unsigned int position)
{
    /*
     * Le principal probleme a resoudre etait de placer le champ de vision
     * correctement par rapport a la nouvelle position du curseur. Du code
     * de saisir_menu_texte a ete reutilise. De plus, si on pointait sur le
     * dernier choix et que le nombre de choix a diminue (cela arrive lors des
     * ventes d'objets), le curseur risque de se retrouver "dans le vide". Un
     * test a donc ete rajoute afin de le placer sur le dernier choix du menu
     * dans ce cas particulier.
     */
    if(position >= menu->nb_choix)
        position = menu->nb_choix - 1;
    do
    {
        if(menu->position_curseur == menu->nb_choix - 1)
            menu->position_curseur = 0;
        else
            menu->position_curseur++;
        /* deplacement du champ de vision des choix */
        if(menu->nb_choix_affiches > 2 &&
           menu->indice_premier_choix + menu->nb_choix_affiches < menu->nb_choix)
        {
            if(menu->position_curseur > menu->indice_premier_choix + menu->nb_choix_affiches - 2)
            menu->indice_premier_choix++;
        }
        else
        {
            if(menu->position_curseur > menu->indice_premier_choix + menu->nb_choix_affiches - 1)
                menu->indice_premier_choix++;
        }
        while(menu->position_curseur < menu->indice_premier_choix)
            menu->indice_premier_choix--;
    } while(menu->position_curseur != position);
}



/**
 * \fn static unsigned int compter_objets_du_type(const struct etat_partie* partie,
                                                  const char type,
                                                  const struct base_donnees* donnees)
 * \brief Permet de savoir combien de combien d'objets
 *        differents d'un type donne possede le joueur
 *
 * \param[in] partie L'adresse de l'etat de la partie
 * \param[in] type Le type d'objet recherche : ARME, VESTE,
 *                 GANT, CHAUSSURE, ACCESSOIRE, ou UTILISABLE, ou encore 0
 *                 si on souhaite compter tous les objets
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 * \return Le nombre d'objets differents du type type que possede le joueur
 *
 * \warning Un objet possede en quantite nulle ne sera pas compte.
 */
static unsigned int compter_objets_du_type(const struct etat_partie* partie,
                                           const char type,
                                           const struct base_donnees* donnees)
{
    struct objet* p_objet;
    unsigned int resultat;

    resultat = 0;
    p_objet = partie->objets_possedes.prem;
    while(p_objet)
    {
        if((type == 0 || get_type_objet(donnees, p_objet->id) == type)
           && p_objet->quantite > 0)
            resultat++;
        p_objet = p_objet->suiv;
    }

    return resultat;
}



/**
 * \fn static unsigned int compter_objets_du_type_magasin(const char type,
                                                          const struct base_donnees* donnees)
 * \brief Similaire a compter_objets_du_type, mais execute la recherche dans
 *        les objets du magasin et non dans l'inventaire du joueur.\n
 *        Sert pour l'achat.
 *
 * \param[in] type Le type d'objet recherche : ARME, VESTE,
 *                 GANT, CHAUSSURE, ACCESSOIRE, ou UTILISABLE, ou encore 0
 *                 si on souhaite compter tous les objets
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 * \return Le nombre d'objets differents du type type qui sont en vente
 */
static unsigned int compter_objets_du_type_magasin(const char type,
                                                   const struct base_donnees* donnees)
{
    int i;
    unsigned int resultat;

    resultat = 0;
    for(i = 0;  i < donnees->objets_en_vente.nb_elements;  i++)
        if(type == 0 || get_type_objet(donnees,
                        donnees->objets_en_vente.tab[i]) == type)
            resultat++;

    return resultat;
}



/**
 * \fn static void modifier_equipement(struct etat_partie* partie,
                                       const int id_objet_a_equiper,
                                       const char champ)
 * \brief Modifie un champ d'equipement
 *
 * \param[in,out] partie L'adresse de l'etat de la partie
 * \param[in] id_objet_a_equiper L'id de l'objet qui doit etre equipe
 * \param[in] champ ARME, VESTE, GANT, CHAUSSURE, ACCESSOIRE, UTIL_1, UTIL_2,
 *            ou UTIL_3. Designe le champ d'equipement a modifier
 */
static void modifier_equipement(struct etat_partie* partie,
                                const int id_objet_a_equiper,
                                const char champ)
{
    int ancien_objet_equipe;

    if(id_objet_a_equiper != VIDE)
        enlever_exemplaires(&partie->objets_possedes, id_objet_a_equiper, 1);
    switch(champ)
    {
        case ARME:
            ancien_objet_equipe = partie->arme;
            partie->arme = id_objet_a_equiper;
            break;
        case VESTE:
            ancien_objet_equipe = partie->veste;
            partie->veste = id_objet_a_equiper;
            break;
        case GANT:
            ancien_objet_equipe = partie->gants;
            partie->gants = id_objet_a_equiper;
            break;
        case CHAUSSURE:
            ancien_objet_equipe = partie->chaussures;
            partie->chaussures = id_objet_a_equiper;
            break;
        case ACCESSOIRE:
            ancien_objet_equipe = partie->accessoire;
            partie->accessoire = id_objet_a_equiper;
            break;
        case UTIL_1:
            ancien_objet_equipe = partie->objets_utilisables[0];
            partie->objets_utilisables[0] = id_objet_a_equiper;
            break;
        case UTIL_2:
            ancien_objet_equipe = partie->objets_utilisables[1];
            partie->objets_utilisables[1] = id_objet_a_equiper;
            break;
        case UTIL_3:
            ancien_objet_equipe = partie->objets_utilisables[2];
            partie->objets_utilisables[2] = id_objet_a_equiper;
            break;
        default:
            break;
    }
    if(ancien_objet_equipe != VIDE)
        ajouter_exemplaires(&partie->objets_possedes, ancien_objet_equipe, 1);
}



/**
 * \fn static void menu_choisir_equipement(struct etat_partie* partie,
                                           const char champ,
                                           const struct base_donnees* donnees)
 * \brief Est appele par menu_equipement afin que le joueur
 *        puisse changer l'objet equipe dans un champ donne
 *
 * \param[in,out] partie L'adresse de l'etat de la partie
 * \param[in] champ ARME, VESTE, GANT, CHAUSSURE, ACCESSOIRE, UTIL_1, UTIL_2,
 *            ou UTIL_3. Designe le champ que le joueur souhaite modifier
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 *
 * \warning Toutes les fonctions commencant par "menu" doivent etre
 *          appelees APRES avoir appele demarrer_texte ! Elles ne le feront pas !
 *          (Et pensez a appeler arreter_texte apres les appels aux menus)
 */
static void menu_choisir_equipement(struct etat_partie* partie,
                                    const char champ,
                                    const struct base_donnees* donnees)
{
    struct menu_texte menu;
    fenetre_texte* fenetre;
    int valeur_choix;
    char saisie;
    unsigned int nb_objets;
    unsigned char nb_choix_speciaux;
    struct objet* p_objet;
    char type;
    unsigned int pos_y;

    fenetre = ouvrir_fenetre_texte(65, 25, 13, 7);

    if(champ == UTIL_1 || champ == UTIL_2 || champ == UTIL_3)
        type = UTILISABLE;
    else
        type = champ;

    nb_objets = compter_objets_du_type(partie, type, donnees);
    /* on ne peut pas enlever son arme sans la remplacer, donc... */
    if(type == ARME)
        nb_choix_speciaux = 1;
    else
        nb_choix_speciaux = 2;
    init_menu_texte(&menu, nb_objets + nb_choix_speciaux, 10, "Changer Equipement", fenetre);

    /* VIDE valant -1, il nous faut mettre le retour a -2... */
    ajouter_choix(&menu, "Retour", -2);
    /* encore une fois, on ne peut pas ne pas avoir d'arme equipee */
    if(type != ARME)
        ajouter_choix(&menu, "Enlever", VIDE);

    /*
     * il faut parcourir l'inventaire et mettre dans le menu tous les
     * objets qui peuvent etre equipes a l'emplacement auquel on s'interesse
     */
    p_objet = partie->objets_possedes.prem;
    while(p_objet)
    {
        if(get_type_objet(donnees, p_objet->id) == type
           && p_objet->quantite > 0)
            ajouter_choix(&menu, get_nom_objet(donnees, p_objet->id), p_objet->id);
        p_objet = p_objet->suiv;
    }

    do
    {
        valeur_choix = get_valeur_choix(&menu);
        vider_fenetre_texte(fenetre);
        afficher_menu_texte(&menu);
        if(valeur_choix != -2)
        {
            /*
             * pour un objet utilisable, on affiche juste les caracteristiques
             * de l'objet en question. Pour le reste, on affiche les
             * changements provoques sur les stats du joueur par l'eventuel
             * changement d'equipement
             */
            pos_y = 14;
            if(type == UTILISABLE)
            {
                if(valeur_choix != VIDE)
                {
                    afficher_stats_objet(fenetre, &pos_y, valeur_choix, donnees);
                    pos_y++;
                    afficher_quantite(fenetre, &pos_y, partie, valeur_choix);
                }
            }
            else
                afficher_evolution_stats(fenetre, &pos_y, partie, valeur_choix, type, donnees);
        }
        saisie = saisir_menu_texte(&menu);
    } while(saisie != ENTREE && saisie != ECHAP);
    /*
     * pour simplifier, un appui sur echap sera
     * traite exactement comme un choix de "Retour"
     */
    if(valeur_choix == -2)
        saisie = ECHAP;
    if(saisie == ENTREE)
        modifier_equipement(partie, valeur_choix, champ);
    detruire_menu_texte(&menu);
    fermer_fenetre_texte(fenetre);
}



/**
 * \fn static void get_quantites_achetable_et_possedee(unsigned int* quantite_achetable,
                                                       unsigned int* quantite_possedee,
                                                       const struct etat_partie* partie,
                                                       const unsigned int id_objet,
                                                       const unsigned int valeur_objet)
 * \brief Permet de savoir de combien d'exemplaires d'un objet donne le joueur
 *        possede, ainsi que combien il peut se permet d'en acheter compte
 *        tenu de son argent et de ce qu'il possede
 *        deja (par rapport a MAX_Q_OBJET)
 *
 * \param[out] quantite_achetable Le nombre d'exemplaires que le joueur peut acheter
 * \param[out] quantite_possedee Le nombre d'exemplaires que le joueur possede actuellement
 * \param[in] partie L'adresse de l'etat de la partie
 * \param[in] id_objet L'id de l'objet concerne
 * \param[in] valeur_objet Le prix d'achat de l'objet concerne
 */
static void get_quantites_achetable_et_possedee(unsigned int* quantite_achetable,
                                                unsigned int* quantite_possedee,
                                                const struct etat_partie* partie,
                                                const unsigned int id_objet,
                                                const unsigned int valeur_objet)
{
    *quantite_achetable = partie->argent / valeur_objet;
    *quantite_possedee = get_quantite_possedee(partie, id_objet);
    if(*quantite_achetable > MAX_Q_OBJET - *quantite_possedee)
        *quantite_achetable = MAX_Q_OBJET - *quantite_possedee;
}



/**
 * \fn static unsigned int menu_choisir_quantite(const unsigned int argent,
                                                 const unsigned int quantite_maximale,
                                                 const unsigned int id_objet,
                                                 const unsigned int valeur_objet,
                                                 const char type_transaction)
 * \brief Permet au joueur de choisir une quantite a
 *        vendre ou a acheter, de 0 a un maximum donne, et affiche le prix
 *        total, l'argent possede, et l'argent apres l'eventuelle transaction
 *
 * \param[in] argent L'argent possede par le joueur
 * \param[in] quantite_maximale La quantite maximale que le
 *                              joueur peut acheter ou vendre
 * \param[in] id_objet L'id de l'objet concerne
 * \param[in] valeur_objet La valeur unitaire de l'objet concerne
 * \param[in] type_transaction ACHAT ou VENTE
 * \return La quantite choisie par le joueur (dans [0 , quantite_maximale])
 *
 * Cette fonction est a utiliser pour l'achat et la vente,
 * mais pas pour les quetes.\n
 * Notez qu'elle calcule elle-meme la quantite maximale en
 * fonction des parametres.\n
 * Une valeur de type_transaction invalide sera interpretee comme VENTE.
 */
static unsigned int menu_choisir_quantite(const unsigned int argent,
                                          const unsigned int quantite_maximale,
                                          const unsigned int id_objet,
                                          const unsigned int valeur_objet,
                                          const char type_transaction)
{
    fenetre_texte* fenetre;
    struct menu_texte menu;
    int i;
    char buffer[32];
    unsigned int valeur_choix;
    char saisie;
    unsigned int pos_y;
    unsigned int total_valeur;
    unsigned int argent_apres;

    fenetre = ouvrir_fenetre_texte(28, 10, 19, 10);
    init_menu_texte(&menu, quantite_maximale + 1, 1, "Quantite ?", fenetre);

    for(i = quantite_maximale;  i >= 0;  i--)
    {
        sprintf(buffer, "x%d", i);
        ajouter_choix(&menu, buffer, i);
    }
    /* on place le curseur sur la quantite 1 */
    set_position_curseur(&menu, quantite_maximale - 1);
    do
    {
        valeur_choix = get_valeur_choix(&menu);
        total_valeur = valeur_choix * valeur_objet;
        if(type_transaction == ACHAT)
            argent_apres = argent - total_valeur;
        else
        {
            if(MAX_ARGENT - total_valeur < argent)
                argent_apres = MAX_ARGENT;
            else
                argent_apres = argent + total_valeur;
        }

        vider_fenetre_texte(fenetre);
        afficher_menu_texte(&menu);
        pos_y = 6;
        sprintf(buffer, "Argent avant : %u", argent);
        afficher_dans_boite(fenetre, &pos_y, buffer);
        sprintf(buffer, "Total :        %u", total_valeur);
        afficher_dans_boite(fenetre, &pos_y, buffer);
        sprintf(buffer, "Argent apres : %u", argent_apres);
        afficher_dans_boite(fenetre, &pos_y, buffer);
        saisie = saisir_menu_texte(&menu);
    } while(saisie != ENTREE && saisie != ECHAP);
    /*
     * un appuie sur une touche de retour en arriere donne
     * le meme resultat que choisir la quantite 0
     */
    if(saisie == ECHAP)
        valeur_choix = 0;

    detruire_menu_texte(&menu);
    fermer_fenetre_texte(fenetre);

    return valeur_choix;
}






/**
 * \fn static unsigned int menu_choisir_quantite_quete(const struct etat_partie* partie,
                                                       const unsigned int quantite_maximale,
                                                       const unsigned int id_quete,
                                                       const struct base_donnees* donnees)
 * \brief Permet au joueur de choisir le nombre de fois
 *        qu'il veut accomplir une quete en une fois
 *
 * \param[in] partie L'adresse de l'etat de la partie
 * \param[in] quantite_maximale Le nombre maximal pouvant etre choisi
 * \param[in] id_quete L'id de la quete concernee
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 * \return Le nombre de fois que le joueur souhaite realiser la quete concernee
 */
static unsigned int menu_choisir_quantite_quete(const struct etat_partie* partie,
                                                const unsigned int quantite_maximale,
                                                const unsigned int id_quete,
                                                const struct base_donnees* donnees)
{
    fenetre_texte* fenetre;
    struct menu_texte menu;
    int i;
    char buffer[64];
    unsigned int quantite;
    char saisie;
    unsigned int pos_y;
    struct objet* p_objet;
    unsigned int quantite_avant, quantite_necessaire;

    fenetre = ouvrir_fenetre_texte(45, 20, 19, 10);
    init_menu_texte(&menu, quantite_maximale + 1, 1, "Quantite ?", fenetre);

    for(i = quantite_maximale;  i >= 0;  i--)
    {
        sprintf(buffer, "x%d", i);
        ajouter_choix(&menu, buffer, i);
    }
    /* on place le curseur sur la quantite 1 */
    set_position_curseur(&menu, quantite_maximale - 1);
    do
    {
        quantite = get_valeur_choix(&menu);
        vider_fenetre_texte(fenetre);
        afficher_menu_texte(&menu);
        pos_y = 6;
        p_objet = get_objets_necessaires(donnees, id_quete);
        while(p_objet)
        {
            quantite_avant = get_quantite_possedee(partie, p_objet->id);
            quantite_necessaire = quantite * p_objet->quantite;
            sprintf(buffer, "%s                         ", get_nom_objet(donnees, p_objet->id));
            sprintf(buffer + 25, "%u   ", quantite_avant);
            sprintf(buffer + 29, "- %u   ", quantite_necessaire);
            sprintf(buffer + 35, "-> %u", quantite_avant - quantite_necessaire);
            afficher_dans_boite(fenetre, &pos_y, buffer);
            p_objet = p_objet->suiv;
        }
        saisie = saisir_menu_texte(&menu);
    } while(saisie != ENTREE && saisie != ECHAP);
    /*
     * un appuie sur une touche de retour en arriere donne
     * le meme resultat que choisir la quantite 0
     */
    if(saisie == ECHAP)
        quantite = 0;

    detruire_menu_texte(&menu);
    fermer_fenetre_texte(fenetre);

    return quantite;
}



/**
 * \fn static unsigned int compter_objets_du_type_quetes(const char type_objets,
                                                         const struct base_donnees* donnees)
 * \brief Compte les quetes qui permettent d'obtenir un objet d'un type donne
 *
 * \param[in] type_objets Le type d'objets auquel on s'interesse : ARME, VESTE,
 *                        GANT, CHAUSSURE, ACCESSOIRE, ou UTILISABLE, ou
 *                        encore 0 si on souhaite compter tous les objets
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 * \return Le nombre de quetes qui permettent d'obtenir un objet du type donne
 */
static unsigned int compter_objets_du_type_quetes(const char type_objets,
                                                  const struct base_donnees* donnees)
{
    int i;
    unsigned int resultat;

    if(type_objets == 0)
        return donnees->tab_quetes.nb_elements;

    resultat = 0;
    for(i = 0;  i < donnees->tab_quetes.nb_elements;  i++)
    {
        if(get_type_objet(donnees, get_recompense(donnees, i)) == type_objets)
            resultat++;
    }
    return resultat;
}



/**
 * \fn static unsigned int compter_quantite_maximale_quete(const struct etat_partie* partie,
                                                           const unsigned int id_quete,
                                                           const struct base_donnees* donnees)
 * \brief Permet de savoir combien de fois le joueur
 *        peut accomplir une quete avec ses objets actuels
 *
 * \param[in] partie L'adresse de l'etat de la partie du joueur
 * \param[in] id_quete L'id de la quete concernee
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 * \return Le nombre de fois que le joueur peut accomplir la quete concernee
 */
static unsigned int compter_quantite_maximale_quete(const struct etat_partie* partie,
                                                    const unsigned int id_quete,
                                                    const struct base_donnees* donnees)
{
    unsigned int quantite_necessaire;
    unsigned int quantite_possedee;
    unsigned int nombre_de_lots;
    unsigned int resultat;
    struct objet* p_objet;
    /* VRAI pour le premier objet, puis FAUX */
    char premier_objet;

    /*
     * Pour chaque objet, on regarde combien de fois
     * le joueur peut founir la quantite demandee.
     * Le resultat est le minimum de ces resultats intermediaires
     */
    premier_objet = VRAI;
    p_objet = get_objets_necessaires(donnees, id_quete);
    while(p_objet)
    {
        quantite_necessaire = p_objet->quantite;
        quantite_possedee = get_quantite_possedee(partie, p_objet->id);
        nombre_de_lots = quantite_possedee / quantite_necessaire;
        if(premier_objet)
        {
            resultat = nombre_de_lots;
            premier_objet = FAUX;
        }
        else
            if(nombre_de_lots < resultat)
                resultat = nombre_de_lots;
        p_objet = p_objet->suiv;
    }

    quantite_possedee = get_quantite_possedee(partie,
                        get_recompense(donnees, id_quete));
    if(resultat + quantite_possedee > MAX_Q_OBJET)
        return MAX_Q_OBJET - quantite_possedee;

    return resultat;
}



/**
 * \fn static void realiser_quete(struct etat_partie* partie,
                                  const unsigned int id_quete,
                                  const unsigned int quantite,
                                  const struct base_donnees* donnees)
 * \brief Met a jour l'inventaire du joueur en lui faisant
 *        accomplir une quete donnee un nombre donne de fois
 *
 * \param[in,out] partie L'adresse de l'etat de la partie
 * \param[in] id_quete L'id de la quete realisee
 * \param[in] quantite Le nombre de fois que la quete est realisee
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 *
 * Cette fonction enleve a l'inventaire les objets necessaires pour la quete et
 * y ajoute la recompense pour la quete, et ce avec un nombre d'exemplaires a
 * chaque fois multiplie par "quantite".
 *
 * \warning Cette fonction ne verifie pas la faisabilite de la quete !
 */
static void realiser_quete(struct etat_partie* partie,
                           const unsigned int id_quete,
                           const unsigned int quantite,
                           const struct base_donnees* donnees)
{
    struct objet* p_objet;

    p_objet = get_objets_necessaires(donnees, id_quete);
    while(p_objet)
    {
        enlever_exemplaires(&partie->objets_possedes,
                            p_objet->id,
                            quantite * p_objet->quantite);
        p_objet = p_objet->suiv;
    }
    ajouter_exemplaires(&partie->objets_possedes,
                        get_recompense(donnees, id_quete),
                        quantite);
}



/**
 * \fn static void afficher_description_quete(fenetre_texte* fenetre,
                                              const struct etat_partie* partie,
                                              const unsigned int id_quete,
                                              const struct base_donnees* donnees)
 * \brief Affiche la description de la recompense d'une quete et le nombre
 *        d'exemplaires de cet objet que le joueur possede
 *
 * \param[in,out] fenetre L'adresse de la fenetre dans laquelle on souhaite ecrire
 * \param[in] partie L'adresse de l'etat de la partie
 * \param[in] id_quete L'indice de la quete concernee dans le tableau de
 *                     quetes de la base de donnees
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 *
 * Cette fonction affiche egalement le nombre d'exemplaires possedes par le
 * joueur pour chaque objet necessaire pour la quete.
 */
static void afficher_description_quete(fenetre_texte* fenetre,
                                       const struct etat_partie* partie,
                                       const unsigned int id_quete,
                                       const struct base_donnees* donnees)
{
    unsigned int pos_y;
    unsigned int recompense;
    unsigned int id_objet;
    struct objet* p_objet;
    char buffer[64];

    recompense = get_recompense(donnees, id_quete);
    pos_y = 15;
    afficher_stats_objet(fenetre, &pos_y, recompense, donnees);
    pos_y++;
    sprintf(buffer, "Possedes : %u", get_quantite_possedee(partie, recompense));
    afficher_dans_boite(fenetre, &pos_y, buffer);
    pos_y++;
    /* affichage de la liste des objets necessaires */
    afficher_dans_boite(fenetre, &pos_y, "Objets necessaires :");
    p_objet = get_objets_necessaires(donnees, id_quete);
    while(p_objet)
    {
        id_objet = p_objet->id;
        sprintf(buffer, "%s                        ", get_nom_objet(donnees, id_objet));
        sprintf(buffer + 24, "x%u   ", p_objet->quantite);
        sprintf(buffer + 29, "(%u)", get_quantite_possedee(partie, id_objet));
        afficher_dans_boite(fenetre, &pos_y, buffer);
        p_objet = p_objet->suiv;
    }
}



/**
 * \fn static void menu_acheter(struct etat_partie* partie,
                                const int type_objets,
                                const struct base_donnees* donnees)
 * \brief Permet au joueur d'acheter des objets d'un type donne
 *
 * \param[in,out] partie L'adresse de l'etat de la partie
 * \param[in] type_objets Le type d'objet qui va etre affiche.
 *                        0 designe tous les types a la fois
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 */
static void menu_acheter(struct etat_partie* partie,
                         const int type_objets,
                         const struct base_donnees* donnees)
{
    unsigned int nb_objets;
    fenetre_texte* fenetre;
    struct menu_texte menu;
    char saisie;
    unsigned int id_objet;
    char buffer[64];
    int valeur_choix;
    unsigned int position_curseur;
    unsigned int pos_y;
    unsigned int quantite;
    unsigned int valeur_objet;
    int i;
    unsigned int quantite_achetable;
    unsigned int quantite_possedee;

    /*
     * attention ! le contenu du magasin ne change pas, on peut donc compter
     * les objets hors de la boucle. Toutefois, ce n'est pas
     * le cas pour la vente !
     */
    nb_objets = compter_objets_du_type_magasin(type_objets, donnees);
    position_curseur = 0;
    do
    {
        fenetre = ouvrir_fenetre_texte(54, 25, 13, 7);
        init_menu_texte(&menu, nb_objets + 1, 10, "Acheter", fenetre);
        ajouter_choix(&menu, "Retour", -1);
        /* creation du menu en parcourant la liste des objets mis en vente */
        for(i = 0;  i < donnees->objets_en_vente.nb_elements;  i++)
        {
            id_objet = donnees->objets_en_vente.tab[i];
            if((type_objets == 0 || get_type_objet(donnees, id_objet) == type_objets))
            {
                valeur_objet = get_prix_achat(donnees, id_objet);
                get_quantites_achetable_et_possedee(&quantite_achetable,
                                                    &quantite_possedee,
                                                    partie, id_objet,
                                                    valeur_objet);
                sprintf(buffer, "%s                        ",
                        get_nom_objet(donnees, id_objet));
                sprintf(buffer + 24, "%u         ", valeur_objet);
                sprintf(buffer + 34, "(x%u)   ", quantite_possedee);
                if(quantite_achetable == 0)
                {
                    if(quantite_possedee == MAX_Q_OBJET)
                        sprintf(buffer + 41, "Deja trop");
                    else
                        sprintf(buffer + 41, "Trop cher");
                }
                ajouter_choix(&menu, buffer, id_objet);
            }
        }

        set_position_curseur(&menu, position_curseur);
        do
        {
            valeur_choix = get_valeur_choix(&menu);
            vider_fenetre_texte(fenetre);
            afficher_menu_texte(&menu);
            pos_y = 15;
            if(valeur_choix != -1 && type_objets != MATERIAU)
            {
                afficher_stats_objet(fenetre, &pos_y, valeur_choix, donnees);
                pos_y++;
            }
            sprintf(buffer, "Argent : %u", partie->argent);
            afficher_dans_boite(fenetre, &pos_y, buffer);
            saisie = saisir_menu_texte(&menu);
        } while(saisie != ENTREE && saisie != ECHAP);
        if(valeur_choix == -1)
            saisie = ECHAP;
        position_curseur = get_position_curseur(&menu);

        if(saisie != ECHAP)
        {
            /* achat */
            valeur_objet = get_prix_achat(donnees, valeur_choix);
            get_quantites_achetable_et_possedee(&quantite_achetable,
                                                &quantite_possedee,
                                                partie, valeur_choix,
                                                valeur_objet);
            /*
             * si le joueur possede deja MAX_Q_OBJET de l'objet choisi ou
             * n'a pas assez d'argent, il ne faut pas afficher le menu de
             * choix de quantite, et aucune transaction n'aura lieu
             */
            if(quantite_achetable > 0)
            {
                quantite = menu_choisir_quantite(partie->argent,
                                             quantite_achetable,
                                             valeur_choix,
                                             valeur_objet, ACHAT);
                partie->argent -= quantite * valeur_objet;
                ajouter_exemplaires(&partie->objets_possedes,
                                    valeur_choix, quantite);
            }
        }

        detruire_menu_texte(&menu);
        fermer_fenetre_texte(fenetre);
    } while(saisie != ECHAP);
}



/**
 * \fn static void menu_vendre(struct etat_partie* partie,
                               const int type_objets,
                               const struct base_donnees* donnees)
 * \brief Permet au joueur de vendre des objets d'un type donne
 *
 * \param[in,out] partie L'adresse de l'etat de la partie
 * \param[in] type_objets Le type d'objet qui va etre affiche.
 *                        0 designe tous les types a la fois
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 */
static void menu_vendre(struct etat_partie* partie,
                        const int type_objets,
                        const struct base_donnees* donnees)
{
    unsigned int nb_objets;
    fenetre_texte* fenetre;
    struct menu_texte menu;
    char saisie;
    struct objet* p_objet;
    char buffer[64];
    int valeur_choix;
    unsigned int position_curseur;
    unsigned int pos_y;
    unsigned int quantite;
    unsigned int total_valeur;
    unsigned int valeur_objet;

    position_curseur = 0;
    do
    {
        nb_objets = compter_objets_du_type(partie, type_objets, donnees);
        fenetre = ouvrir_fenetre_texte(40, 27, 13, 7);
        init_menu_texte(&menu, nb_objets + 1, 10, "Vendre", fenetre);
        ajouter_choix(&menu, "Retour", -1);
        /* creation du menu en parcourant l'inventaire */
        p_objet = partie->objets_possedes.prem;
        while(p_objet)
        {
            if((type_objets == 0 || get_type_objet(donnees, p_objet->id) == type_objets)
               && p_objet->quantite > 0)
            {
                sprintf(buffer, "%ux   ", p_objet->quantite);
                sprintf(buffer + 5, "%s", get_nom_objet(donnees, p_objet->id));
                ajouter_choix(&menu, buffer, p_objet->id);
            }
            p_objet = p_objet->suiv;
        }

        /* utilisation du menu */
        set_position_curseur(&menu, position_curseur);
        do
        {
            valeur_choix = get_valeur_choix(&menu);
            vider_fenetre_texte(fenetre);
            afficher_menu_texte(&menu);
            pos_y = 15;
            if(valeur_choix != -1)
            {
                if(type_objets != MATERIAU)
                {
                    afficher_stats_objet(fenetre, &pos_y, valeur_choix, donnees);
                    pos_y++;
                }
                sprintf(buffer, "Valeur : %u", get_prix_vente(donnees, valeur_choix));
                afficher_dans_boite(fenetre, &pos_y, buffer);
                pos_y++;
            }
            sprintf(buffer, "Argent : %u", partie->argent);
            afficher_dans_boite(fenetre, &pos_y, buffer);
            saisie = saisir_menu_texte(&menu);
        } while(saisie != ENTREE && saisie != ECHAP);
        /* fin d'utilisation du menu */
        if(valeur_choix == -1)
            saisie = ECHAP;
        position_curseur = get_position_curseur(&menu);

        if(saisie != ECHAP)
        {
            /* c'est ici que l'objet va reellement etre vendu */
            valeur_objet = get_prix_vente(donnees, valeur_choix);
            quantite = menu_choisir_quantite(partie->argent,
                                             get_quantite_possedee(partie, valeur_choix),
                                             valeur_choix,
                                             valeur_objet, VENTE);
            enlever_exemplaires(&partie->objets_possedes,
                                valeur_choix, quantite);
            total_valeur = quantite * valeur_objet;
            if(MAX_ARGENT - total_valeur < partie->argent)
                partie->argent = MAX_ARGENT;
            else
                partie->argent += total_valeur;
        }

        detruire_menu_texte(&menu);
        fermer_fenetre_texte(fenetre);
    } while(saisie != ECHAP);
}



/**
 * \fn static void menu_quetes(struct etat_partie* partie,
                               const int type_objets,
                               const struct base_donnees* donnees)
 * \brief Permet au joueur de faire des quetes
 *        permettant d'obtenir un objet d'un type donne
 *
 * \param[in,out] partie L'adresse de l'etat de la partie
 * \param[in] type_objets Le type des objets obtenus grace aux
 *                        quetes qui seront affiches.
 *                        0 designe tous les types a la fois
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 */
static void menu_quetes(struct etat_partie* partie,
                        const int type_objets,
                        const struct base_donnees* donnees)
{
    fenetre_texte* fenetre;
    struct menu_texte menu;
    char saisie;
    unsigned int nb_quetes;
    unsigned int position_curseur;
    int valeur_choix;
    int i;
    unsigned int recompense;
    unsigned int quantite_maximale;
    unsigned int quantite_demandee;
    char buffer[64];

    nb_quetes = compter_objets_du_type_quetes(type_objets, donnees);
    position_curseur = 0;
    do
    {
        fenetre = ouvrir_fenetre_texte(47, 34, 13, 7);
        init_menu_texte(&menu, nb_quetes + 1, 10, "Quetes", fenetre);
        ajouter_choix(&menu, "Retour", -1);

        /* creation du menu en parcourant la liste des quetes */
        for(i = 0;  i < donnees->tab_quetes.nb_elements;  i++)
        {
            recompense = get_recompense(donnees, i);
            if(type_objets == 0 ||
               type_objets == get_type_objet(donnees, recompense))
            {
                sprintf(buffer, "%s                             ",
                        get_nom_objet(donnees, recompense));
                if(compter_quantite_maximale_quete(partie, i, donnees) > 0)
                    sprintf(buffer + 29, "(Realisable !)");
                /*
                 * notez bien que la valeur des choix est l'indice de la quete dans
                 * le tableau de quetes, et non l'id de la recompense
                 */
                ajouter_choix(&menu, buffer, i);
            }
        }

        /* utilisation du menu */
        set_position_curseur(&menu, position_curseur);
        do
        {
            valeur_choix = get_valeur_choix(&menu);
            vider_fenetre_texte(fenetre);
            afficher_menu_texte(&menu);
            if(valeur_choix != -1)
                afficher_description_quete(fenetre, partie, valeur_choix, donnees);
            saisie = saisir_menu_texte(&menu);
        } while(saisie != ENTREE && saisie != ECHAP);
        if(valeur_choix == -1)
            saisie = ECHAP;
        position_curseur = get_position_curseur(&menu);
        /* fin d'utilisation du menu */

        if(saisie != ECHAP)
        {
            quantite_maximale = compter_quantite_maximale_quete(partie,
                                                                valeur_choix,
                                                                donnees);
            if(quantite_maximale > 0)
            {
                /* realisation de la quete */
                quantite_demandee =
                    menu_choisir_quantite_quete(partie, quantite_maximale,
                                                valeur_choix, donnees);
                /*
                 * il faut verifier que la quantite demandee n'est pas nulle,
                 * car, sinon, on ajouterait une quantite d'objets nulle a
                 * l'inventaire. Cela ne pose pas de reels problemes, mais
                 * la recompense de la quete ne serait alors plus consideree
                 * comme un nouvel objet lors de son obtention, car elle serait
                 * presente dans l'inventaire avec une quantite nulle
                 */
                if(quantite_demandee > 0)
                    realiser_quete(partie, valeur_choix, quantite_demandee, donnees);
            }
        }

        detruire_menu_texte(&menu);
        fermer_fenetre_texte(fenetre);
    } while(saisie != ECHAP);
}



/**
 * \fn static void sauvegarder(const struct etat_partie* partie)
 * \brief Affiche une fenetre indiquant l'avancement de la sauvegarde et
 *        execute la sauvegarde en question
 *
 * \param[in] partie L'adresse de l'etat de la partie
 */
static void sauvegarder(const struct etat_partie* partie)
{
    fenetre_texte* fenetre;
    unsigned int pos_y;
    FILE* fichier;

    fenetre = ouvrir_fenetre_texte(27, 7, 7, 4);

    afficher_titre_fenetre(fenetre, "Sauvegarde");
    pos_y = 1;
    afficher_dans_boite(fenetre, &pos_y, "Creation du fichier...");
    fichier = creer_fichier_sauvegarde(partie->nom);
    afficher_dans_boite(fenetre, &pos_y, "Ecriture des donnees...");
    remplir_fichier_sauvegarde(partie, fichier);
    afficher_dans_boite(fenetre, &pos_y, "Fermeture du fichier...");
    fermer_sauvegarde(fichier);
    afficher_dans_boite(fenetre, &pos_y, "Termine");
    afficher_dans_boite(fenetre, &pos_y, "Appuyez sur Entree");
    attendre_entree(fenetre);

    fermer_fenetre_texte(fenetre);
}



/**
 * \fn static void saisir_chaine_mode_texte(fenetre_texte* fenetre,
                                            const unsigned int x,
                                            const unsigned int y,
                                            char chaine[32])
 * \brief Permet au joueur de saisir une chaine de 31 caracteres
 *        maximum, qui apparaitra a un endroit donne
 *
 * \param[in] fenetre La fenetre sur laquelle la chaine va
 *                    s'afficher pendant que le joueur la tape
 * \param[in] x Coordonnee horizontale, en nombre de caracteres,
 *              de l'endroit ou apparaitra la chaine
 * \param[in] y Coordonnee verticale, en nombre de caracteres,
 *              de l'endroit ou apparaitra la chaine
 * \param[out] chaine La chaine dans laquelle le joueur va ecrire
 */
static void saisir_chaine_mode_texte(fenetre_texte* fenetre,
                                     const unsigned int x,
                                     const unsigned int y,
                                     char chaine[32])
{
    echo();
    curs_set(1);
    mvwgetnstr(fenetre, 1, 2, chaine, 31);
    curs_set(0);
    noecho();
}



/**
 * \fn static void menu_entrer_nom_nouvelle_partie(char nom_partie[32])
 * \brief Permet au joueur d'entrer un nom pour une nouvelle partie
 *        (max 31 + caractere de fin de chaine)
 *
 * \param[out] nom_partie La chaine dans laquelle le joueur va ecrire
 */
static void menu_entrer_nom_nouvelle_partie(char nom_partie[32])
{
    fenetre_texte* fenetre;

    do
    {
        fenetre = ouvrir_fenetre_texte(35, 3, 7, 4);
        afficher_titre_fenetre(fenetre, "Entrez Votre Nom");
        saisir_chaine_mode_texte(fenetre, 2, 1, nom_partie);
        fermer_fenetre_texte(fenetre);
        /*
         * on boucle tant que l'utilisateur rentre un nom vide ou
         * un nom qui serait confondu avec un fichier cache d'unix
         */
    } while(!nom_partie[0] || nom_partie[0] == '.');
}



/**
 * \fn static void menu_choisir_partie(char nom_partie[])
 * \brief Permet au joueur de choisir une des parties enregistrees
 *
 * \param[out] nom_partie La chaine qui recevra le nom de la partie choisie
 *
 * Notez que le joueur peut faire un retour en arriere sans choisir
 * de partie. Dans ce cas, la chaine nom_partie ne sera pas modifiee.
 */
static void menu_choisir_partie(char nom_partie[])
{
    DIR *dossier;
    struct dirent *infos;
    unsigned int nb_parties;
    char noms_des_parties[MAX_NOMBRE_DE_PARTIES][256];
    char saisie;
    int resultat;
    fenetre_texte* fenetre;
    struct menu_texte menu;
    int i;

    if(!(dossier = opendir("donnees/parties")))
    {
        printf("Erreur lors de l'ouverture du dossier donnees/parties\n");
        exit(EXIT_FAILURE);
    }
    nb_parties = 0;
    while((infos = readdir(dossier)) && nb_parties < MAX_NOMBRE_DE_PARTIES)
    {
        /* on ignore les fichiers caches d'unix */
        if(infos->d_name[0] != '.')
        {
            strcpy(noms_des_parties[nb_parties], infos->d_name);
            nb_parties++;
        }
    }
    closedir(dossier);

    fenetre = ouvrir_fenetre_texte(35, 11, 7, 4);
    init_menu_texte(&menu, nb_parties + 1, 6, "Charger Une Partie", fenetre);
    ajouter_choix(&menu, "Retour", -1);
    for(i = 0;  i < nb_parties;  i++)
        ajouter_choix(&menu, noms_des_parties[i], i);
    do
    {
        vider_fenetre_texte(fenetre);
        afficher_menu_texte(&menu);
        saisie = saisir_menu_texte(&menu);
    } while(saisie != ENTREE && saisie != ECHAP);
    resultat = get_valeur_choix(&menu);
    if(saisie != ECHAP && resultat != -1)
        strcpy(nom_partie, noms_des_parties[resultat]);
    detruire_menu_texte(&menu);
    fermer_fenetre_texte(fenetre);
}



char menu_bataille(void)
{
    struct menu_texte menu;
    unsigned int pos_y;
    fenetre_texte* fenetre;
    char resultat;

    fenetre = ouvrir_fenetre_texte(34, 10, 15, 15);
    init_menu_texte(&menu, 2, 2, "Pause", fenetre);
    ajouter_choix(&menu, "Continuer", PAS_TERMINEE);
    ajouter_choix(&menu, "Abandonner", DEFAITE);
    do
    {
        vider_fenetre_texte(fenetre);
        afficher_menu_texte(&menu);
        pos_y = 6;
        afficher_dans_boite(fenetre, &pos_y, "Abandonner vous fera perdre la");
        afficher_dans_boite(fenetre, &pos_y, "bataille et vous renverra a la");
        afficher_dans_boite(fenetre, &pos_y, "Carte du monde.");
    } while(saisir_menu_texte(&menu) != ENTREE);
    resultat = get_valeur_choix(&menu);
    detruire_menu_texte(&menu);
    fermer_fenetre_texte(fenetre);

    return resultat;
}



char menu_carte_monde(struct etat_partie* partie,
                      const struct base_donnees* donnees)
{
    struct menu_texte menu;
    fenetre_texte* fenetre;
    fenetre_texte* fenetre_stats;
    char resultat;
    char saisie;

    fenetre = ouvrir_fenetre_texte(23, 11, 1, 1);
    init_menu_texte(&menu, 7, 7, "Menu Carte Monde", fenetre);

    ajouter_choix(&menu, "Continuer", 0);
    ajouter_choix(&menu, "Equipement", 1);
    ajouter_choix(&menu, "Achat", 2);
    ajouter_choix(&menu, "Vente", 3);
    ajouter_choix(&menu, "Quetes", 4);
    ajouter_choix(&menu, "Sauvegarder", 5);
    ajouter_choix(&menu, "Quitter le jeu", -1);
    do
    {
        fenetre_stats = afficher_stats_joueur(1, 41, partie, donnees);
        do
        {
            vider_fenetre_texte(fenetre);
            afficher_menu_texte(&menu);
            saisie = saisir_menu_texte(&menu);
        } while(saisie != ENTREE && saisie != ECHAP);
        resultat = get_valeur_choix(&menu);
        if(saisie == ECHAP)
            resultat = 0;
        switch(resultat)
        {
            case 1:
                menu_equipement(partie, donnees);
                break;
            case 2:
                menu_choisir_categorie(partie, 'a', donnees);
                break;
            case 3:
                menu_choisir_categorie(partie, 'v', donnees);
                break;
            case 4:
                menu_choisir_categorie(partie, 'q', donnees);
                break;
            case 5:
                sauvegarder(partie);
                break;
            default:
                break;
        }
        fermer_fenetre_texte(fenetre_stats);
    } while(resultat != 0 && resultat != -1);

    detruire_menu_texte(&menu);
    fermer_fenetre_texte(fenetre);

    if(resultat == -1)
        return -1;
    return 0;
}



char menu_exploration(struct etat_partie* partie,
                      const struct base_donnees* donnees)
{
    struct menu_texte menu;
    fenetre_texte* fenetre;
    fenetre_texte* fenetre_stats;
    char resultat;
    char saisie;

    fenetre = ouvrir_fenetre_texte(23, 10, 1, 1);
    init_menu_texte(&menu, 6, 6, "Menu Exploration", fenetre);

    ajouter_choix(&menu, "Continuer", 0);
    ajouter_choix(&menu, "Equipement", 1);
    ajouter_choix(&menu, "Achat", 2);
    ajouter_choix(&menu, "Vente", 3);
    ajouter_choix(&menu, "Quetes", 4);
    ajouter_choix(&menu, "Sortir de la region", -1);
    do
    {
        fenetre_stats = afficher_stats_joueur(1, 41, partie, donnees);
        do
        {
            vider_fenetre_texte(fenetre);
            afficher_menu_texte(&menu);
            saisie = saisir_menu_texte(&menu);
        } while(saisie != ENTREE && saisie != ECHAP);
        resultat = get_valeur_choix(&menu);
        if(saisie == ECHAP)
            resultat = 0;
        switch(resultat)
        {
            case 1:
                menu_equipement(partie, donnees);
                break;
            case 2:
                menu_choisir_categorie(partie, 'a', donnees);
                break;
            case 3:
                menu_choisir_categorie(partie, 'v', donnees);
                break;
            case 4:
                menu_choisir_categorie(partie, 'q', donnees);
                break;
            default:
                break;
        }
        fermer_fenetre_texte(fenetre_stats);
    } while(resultat != 0 && resultat != -1);

    detruire_menu_texte(&menu);
    fermer_fenetre_texte(fenetre);

    return resultat;
}



void menu_equipement(struct etat_partie* partie,
                     const struct base_donnees* donnees)
{
    struct menu_texte menu;
    fenetre_texte* fenetre;
    unsigned int pos_y;
    int id_objet;
    int valeur_choix;
    char saisie;

    fenetre = ouvrir_fenetre_texte(29, 21, 7, 4);
    init_menu_texte(&menu, 9, 9, "Equipement", fenetre);
    ajouter_choix(&menu, "Arme", ARME);
    ajouter_choix(&menu, "Veste", VESTE);
    ajouter_choix(&menu, "Gants", GANT);
    ajouter_choix(&menu, "Chaussures", CHAUSSURE);
    ajouter_choix(&menu, "Accessoire", ACCESSOIRE);
    ajouter_choix(&menu, "Objet utilisable avec F1", UTIL_1);
    ajouter_choix(&menu, "Objet utilisable avec F2", UTIL_2);
    ajouter_choix(&menu, "Objet utilisable avec F3", UTIL_3);
    ajouter_choix(&menu, "Retour", -1);
    do
    {
        do
        {
            vider_fenetre_texte(fenetre);
            afficher_menu_texte(&menu);
            valeur_choix = get_valeur_choix(&menu);
            if(valeur_choix != -1)
            {
                id_objet = get_equipement(partie, valeur_choix);
                pos_y = 13;
                if(id_objet == VIDE)
                    afficher_dans_boite(fenetre, &pos_y, "<Emplacement vide>");
                else
                    afficher_stats_objet(fenetre, &pos_y, id_objet, donnees);
            }
            saisie = saisir_menu_texte(&menu);
        } while(saisie != ENTREE && saisie != ECHAP);
        /*
         * pour simplifier, un appui sur echap sera
         * traite exactement comme un choix de "Retour"
         */
        if(valeur_choix == -1)
            saisie = ECHAP;
        if(saisie == ENTREE)
            menu_choisir_equipement(partie, valeur_choix, donnees);
    } while(saisie != ECHAP);
    detruire_menu_texte(&menu);
    fermer_fenetre_texte(fenetre);
}



void menu_ameliorer_competences(struct etat_partie* partie,
                                unsigned int nb_points)
{
    struct menu_texte menu;
    fenetre_texte* fenetre;
    unsigned int pos_y;
    char buffer[128];
    unsigned int position_curseur;
    unsigned int valeur_stats;
    int valeur_choix;

    position_curseur = 0;
    do
    {
        /*
         * pour chaque statistique, on verifie si le joueur n'a
         * pas deja atteint le maximum autorise. Si c'est le cas, on utilise
         * la valeur -1 pour le choix correspondant dans le menu
         */
        fenetre = ouvrir_fenetre_texte(36, 35, 7, 4);
        init_menu_texte(&menu, 4, 4, "Level Up !", fenetre);

        valeur_stats = get_pv_max_perm(partie);
        sprintf(buffer, "Points de vie :       %u -> +%u",
                valeur_stats, PTS_VIE_PAR_POINT);
        if(valeur_stats == VALEUR_DEPART_PV + 94 * PTS_VIE_PAR_POINT)
        {
            sprintf(buffer + 16, "Maximum atteint");
            ajouter_choix(&menu, buffer, -1);
        }
        else
            ajouter_choix(&menu, buffer, POINTS_VIE);
        valeur_stats = get_vitesse_deplacement_perm(partie);
        sprintf(buffer, "Vitesse deplacement : %u -> +1  ",
                valeur_stats);
        if(valeur_stats == 99)
        {
            sprintf(buffer + 22, "Maximum atteint");
            ajouter_choix(&menu, buffer, -1);
        }
        else
            ajouter_choix(&menu, buffer, VITESSE_DEPLACEMENT);
        valeur_stats = get_puissance_perm(partie);
        sprintf(buffer, "Puissance :           %u -> +1  ",
                valeur_stats);
        if(valeur_stats == 99)
        {
            sprintf(buffer + 22, "Maximum atteint");
            ajouter_choix(&menu, buffer, -1);
        }
        else
            ajouter_choix(&menu, buffer, PUISSANCE);
        valeur_stats = get_vitesse_charge_perm(partie);
        sprintf(buffer, "Vitesse charge        %u -> +1  ",
                valeur_stats);
        if(valeur_stats == 99)
        {
            sprintf(buffer + 22, "Maximum atteint");
            ajouter_choix(&menu, buffer, -1);
        }
        else
            ajouter_choix(&menu, buffer, VITESSE_CHARGE);
        set_position_curseur(&menu, position_curseur);

        do
        {
            vider_fenetre_texte(fenetre);
            afficher_menu_texte(&menu);
            valeur_choix = get_valeur_choix(&menu);
            pos_y = 8;
            afficher_dans_boite(fenetre, &pos_y, "Choisissez une");
            afficher_dans_boite(fenetre, &pos_y, "competence a ameliorer");
            pos_y++;
            sprintf(buffer, "Ameliorations restantes : %u", nb_points);
            afficher_dans_boite(fenetre, &pos_y, buffer);
        } while(saisir_menu_texte(&menu) != ENTREE || valeur_choix == -1);

        switch(valeur_choix)
        {
            case POINTS_VIE:
                partie->statistiques.pv_max += PTS_VIE_PAR_POINT;
                break;
            case VITESSE_DEPLACEMENT:
                partie->statistiques.vitesse_deplacement++;
                break;
            case PUISSANCE:
                partie->statistiques.puissance++;
                break;
            case VITESSE_CHARGE:
                partie->statistiques.vitesse_charge++;
                break;
            default:
                printf("Erreur 1 dans menu_ameliorer_competences\n");
                exit(EXIT_FAILURE);
                break;
        }
        position_curseur = get_position_curseur(&menu);
        nb_points--;
        detruire_menu_texte(&menu);
        fermer_fenetre_texte(fenetre);
    } while(nb_points > 0);
}



void menu_choisir_categorie(struct etat_partie* partie,
                            const char menu_suivant,
                            const struct base_donnees* donnees)
{
    struct menu_texte menu;
    fenetre_texte* fenetre;
    char saisie;
    int valeur_choix;
    unsigned int position_curseur;

    position_curseur = 0;
    do
    {
        fenetre = ouvrir_fenetre_texte(22, 13, 7, 4);
        init_menu_texte(&menu, 9, 9, "Chercher dans...", fenetre);
        ajouter_choix(&menu, "Tous les objets", 0);
        ajouter_choix(&menu, "Materiaux", MATERIAU);
        ajouter_choix(&menu, "Armes", ARME);
        ajouter_choix(&menu, "Vestes", VESTE);
        ajouter_choix(&menu, "Gants", GANT);
        ajouter_choix(&menu, "Chaussures", CHAUSSURE);
        ajouter_choix(&menu, "Accessoires", ACCESSOIRE);
        ajouter_choix(&menu, "Objets utilisables", UTILISABLE);
        ajouter_choix(&menu, "Retour", -1);
        set_position_curseur(&menu, position_curseur);
        do
        {
            vider_fenetre_texte(fenetre);
            afficher_menu_texte(&menu);
            saisie = saisir_menu_texte(&menu);
        } while(saisie != ENTREE && saisie != ECHAP);
        valeur_choix = get_valeur_choix(&menu);
        if(valeur_choix == -1)
            saisie = ECHAP;
        if(saisie != ECHAP)
        {
            switch(menu_suivant)
            {
                case 'a':
                    menu_acheter(partie, valeur_choix, donnees);
                    break;
                case 'v':
                    menu_vendre(partie, valeur_choix, donnees);
                    break;
                case 'q':
                    menu_quetes(partie, valeur_choix, donnees);
                    break;
                default:
                    printf("Erreur 1 dans menu_choisir_categorie\n");
                    exit(EXIT_FAILURE);
                    break;
            }
        }
        position_curseur = get_position_curseur(&menu);
        detruire_menu_texte(&menu);
        fermer_fenetre_texte(fenetre);
    } while(saisie != ECHAP);
}



char menu_principal(struct etat_partie* partie,
                    const struct base_donnees* donnees)
{
    struct menu_texte menu;
    fenetre_texte* fenetre;
    char saisie;
    int valeur_choix;
    char nom_partie[32];
    unsigned int position_curseur;

    position_curseur = 0;
    do
    {
        fenetre = ouvrir_fenetre_texte(22, 8, 1, 1);
        init_menu_texte(&menu, 4, 4, "Menu Principal", fenetre);
        ajouter_choix(&menu, "Nouvelle partie", 0);
        ajouter_choix(&menu, "Charger partie", 1);
        ajouter_choix(&menu, "Aide", 2);
        ajouter_choix(&menu, "Quitter", -1);
        set_position_curseur(&menu, position_curseur);
        do
        {
            vider_fenetre_texte(fenetre);
            afficher_menu_texte(&menu);
            saisie = saisir_menu_texte(&menu);
        } while(saisie != ENTREE && saisie != ECHAP);
        valeur_choix = get_valeur_choix(&menu);
        if(valeur_choix == -1)
            saisie = ECHAP;
        if(saisie != ECHAP)
        {
            switch(valeur_choix)
            {
                case 0:
                    menu_entrer_nom_nouvelle_partie(nom_partie);
                    charger_partie(partie, nom_partie, VRAI);
                    detruire_menu_texte(&menu);
                    fermer_fenetre_texte(fenetre);
                    return JOUER;
                    break;
                case 1:
                    nom_partie[0] = '\0';
                    menu_choisir_partie(nom_partie);
                    /*
                     * si le joueur ne choisit aucune des parties
                     * proposees (Retour), nom_partie reste vide,
                     * et il ne faut rien faire
                     */
                    if(nom_partie[0])
                    {
                        charger_partie(partie, nom_partie, FAUX);
                        detruire_menu_texte(&menu);
                        fermer_fenetre_texte(fenetre);
                        return JOUER;
                    }
                    break;
                case 2:
                    afficher_aide(donnees->ecran, donnees->font_utilisee);
                    break;
                default:
                    printf("Erreur 1 dans menu_principal\n");
                    printf("Le choix etait %d\n", valeur_choix);
                    exit(EXIT_FAILURE);
                    break;
            }
        }
        position_curseur = get_position_curseur(&menu);
        detruire_menu_texte(&menu);
        fermer_fenetre_texte(fenetre);
    } while(saisie != ECHAP);
    return QUITTER;
}


